package pl.com.qapps.datastore.berkeley.impl;

import com.google.appengine.api.datastore.AccessibilityHelper;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.storage.onestore.v3.OnestoreEntity.EntityProto;
import com.google.storage.onestore.v3.OnestoreEntity.Reference;
import com.sleepycat.bind.EntityBinding;
import com.sleepycat.bind.EntryBinding;
import com.sleepycat.bind.tuple.TupleBinding;
import com.sleepycat.bind.tuple.TupleInput;
import com.sleepycat.bind.tuple.TupleOutput;
import com.sleepycat.je.DatabaseEntry;

class EntityBindings {

	static final EntityBinding<EntityProto> ENTITY_BINDING = new EntityPbBinding();
	static final EntityKeyBinding ENTITY_KEY_BINDING = new EntityKeyBinding();
	static final KeyPbBinding KEY_PB_BINDING = new KeyPbBinding();

	static class EntityKeyBinding extends TupleBinding<Key> {

		@Override
		public Key entryToObject(TupleInput input) {

			return createChildKey(null, input);
		}

		private Key createChildKey(Key parent, TupleInput input) {

			String kind = input.readString();
			Key key;
			int type = input.read();
			if (type == 0) {
				key = KeyFactory.createKey(parent, kind, input.readLong());
			} else {
				key = KeyFactory.createKey(parent, kind, input.readString());
			}
			if (input.read() == 47) {
				return createChildKey(key, input);
			} else
				return key;
		}

		@Override
		public void objectToEntry(Key object, TupleOutput output) {

			if (object == null)
				throw new NullPointerException("serialized key is null");
			if (!object.isComplete())
				throw new IllegalArgumentException(
						"cannot serialize incomplete key: " + object);

			if (object.getParent() != null) {
				objectToEntry(object.getParent(), output);
				output.write(47);
			}
			output.writeString(object.getKind());
			if (object.getId() > 0) {
				output.write(0);
				output.writeLong(object.getId());
			} else {
				output.write(1);
				output.writeString(object.getName());
			}
		}
	}

	static class KeyPbBinding implements EntryBinding<Reference> {

		@Override
		public Reference entryToObject(DatabaseEntry entry) {
			// Reference keyPb = new Reference();
			// keyPb.parseFrom(entry.getData());
			// return keyPb;
			throw new UnsupportedOperationException("not supported");
		}

		@Override
		public void objectToEntry(Reference object, DatabaseEntry entry) {
			ENTITY_KEY_BINDING.objectToEntry(AccessibilityHelper.createFromPb(object),
					entry);
		}
	}

	static class EntityPbBinding implements EntityBinding<EntityProto> {

		@Override
		public EntityProto entryToObject(DatabaseEntry key, DatabaseEntry data) {
			// the key is not necessary unless we do not store him with the entity
			EntityProto entityPb = new EntityProto();
			entityPb.parseFrom(data.getData());
			return entityPb;
		}

		@Override
		public void objectToKey(EntityProto object, DatabaseEntry key) {
			ENTITY_KEY_BINDING.objectToEntry(
					AccessibilityHelper.createFromPb(object.getKey()), key);
		}

		@Override
		public void objectToData(EntityProto object, DatabaseEntry data) {
			data.setData(object.toByteArray());
		}

	}
}
